#include "stdafx.h"
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/execution_monitor.hpp>
#include "bmock_control_tools_mock.hpp"
#include "bmock.hpp"
#include <stdlib.h>

struct flex_tester2 {
	flex_tester2() {
		BMOCK_CREATE_MOCK(*);		
	}
	void flex_tester_method();
};

static const int expected1 = 1;
static const int expected2 = 2;
static const int expected3 = 3;
static const int expected4 = 4;
static const int actual1 = 1;
static const int actual2 = 2;
static const int actual3 = 3;
static const int actual4 = 4;
static const int res = 11;
static int res_actual;

static int f(int x, int y) {
	MOCK(2, (IN(x), IN(y)));
	return 1;
}
static void g(int x) {
	MOCK_VOID(1, (IN(x)));
}
static int non_strict_f(int x) {
	MOCK(1, (IN(x)));
	return 1;
}
void flex_tester2::flex_tester_method() {
	MOCK_VOID(0,());
}

BMOCK_TEST(flex_tester2, test_strict_mocks_expectation_done_minimum_calls) {
	BMOCK_EXPECT(g(expected1));
	BMOCK_REPEAT_RANGE(1,3);
	BMOCK_EXPECT(g(expected2));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected2));
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_strict_mocks_expectation_done_minimum_calls_non_strict_area_after) {
	BMOCK_EXPECT(g(expected1));
	BMOCK_REPEAT_RANGE(1,3);
	BMOCK_NON_STRICT {
		BMOCK_EXPECT(g(expected2));
	}
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected2));
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_strict_mocks_first_area_becomes_empty_because_last_expectation_done_minimum_calls) {
	BMOCK_STRICT {
		BMOCK_EXPECT(g(expected1));
		BMOCK_REPEAT_RANGE(0,3);
	}
	BMOCK_STRICT {
		BMOCK_EXPECT(g(expected2));
	}
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(expected2));
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_non_strict_mocks_expectation_area_is_done) {
	BMOCK_NON_STRICT {
		BMOCK_EXPECT(g(expected1));
		BMOCK_REPEAT_RANGE(1,3);
	}
	BMOCK_NON_STRICT {
		BMOCK_EXPECT(g(expected2));
	}
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected2));
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_mocked_area_becomes_empty_check_if_non_strict) {
	int y = 0;

	BMOCK_EXPECT(g(y));
	BMOCK_REPEAT_RANGE(1,3);
	BMOCK_EXPECT_RETURN_NON_STRICT(y,f(y,y));
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(y));
	BOOST_CHECK_NO_THROW(f(y,y));
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_expectation_and_stub_finished_expectation_becomes_stub) {
	BMOCK_EXPECT(g(0));
	BMOCK_REPEAT(2);
	BMOCK_STUB(g(0));
	BMOCK_REPLAY;
	for (int i = 0; i < 2; i++)
		BOOST_CHECK_NO_THROW(g(0));		//expectation
	for (int i = 0; i < 5; i++)
		BOOST_CHECK_NO_THROW(g(i));		//stub
	BMOCK_VERIFY;

}

BMOCK_TEST(flex_tester2, test_expectation_and_stub_finished_non_strict_expectation_becomes_stub) {
	BMOCK_NON_STRICT {
		for (int i = 0; i < 5; i++)
			BMOCK_EXPECT(g(i));
	}
	BMOCK_STUB(g(0));
	BMOCK_REPLAY;
	for (int i = 0; i < 5; i++)
		BOOST_CHECK_NO_THROW(g(4-i));		//non_strict expectation
	for (int i = 10; i < 15; i++)
		BOOST_CHECK_NO_THROW(g(i));			//stub
	BMOCK_VERIFY;

}

BMOCK_TEST(flex_tester2, test_areas) {
	BMOCK_NON_STRICT { 	
		BMOCK_EXPECT_RETURN(res, f(expected1,expected1));	
		BMOCK_EXPECT(g(expected1));
		BMOCK_EXPECT_RETURN(res+2, f(expected1,expected1));
	}		
	BMOCK_STRICT {		
		BMOCK_EXPECT(g(expected1));	
		BMOCK_EXPECT(g(expected2));
		BMOCK_EXPECT(flex_tester_method());
	}
	BMOCK_NON_STRICT {
		BMOCK_EXPECT(g(expected1));
		BMOCK_EXPECT(g(expected2));
		BMOCK_EXPECT_RETURN(res, f(expected2,expected2));	
		BMOCK_EXPECT(g(expected3));
		BMOCK_EXPECT(g(expected4));
	}
	
	BMOCK_REPLAY;

	{
		BOOST_CHECK_NO_THROW(g(actual1));		
		const int resActual1 = f(expected1,expected1);
		const int resActual2 = f(expected1,expected1);
		BOOST_CHECK_EQUAL(abs(resActual1 - resActual2), 2);		//the difference should be 2 or -2 according to which expectation was used first
	}
	{
		BOOST_CHECK_NO_THROW(g(actual1));
		BOOST_CHECK_NO_THROW(g(actual2));
		BOOST_CHECK_NO_THROW(flex_tester_method());		
	}
	{	
		BOOST_CHECK_NO_THROW(res_actual = f(actual2,actual2));
		BOOST_CHECK_NO_THROW(g(actual4));
		BOOST_CHECK_NO_THROW(g(actual3));
		BOOST_CHECK_NO_THROW(g(actual2));
		BOOST_CHECK_NO_THROW(g(actual1));

		BOOST_CHECK_EQUAL(res_actual,res);
	}
	
	BMOCK_VERIFY;	
}

BMOCK_TEST(flex_tester2, test_non_strict_expectation) {
	BMOCK_NON_STRICT { 	
		BMOCK_EXPECT_RETURN(res, f(expected1,expected1));	
		BMOCK_EXPECT(g(expected1));
	}
	
	BMOCK_EXPECT_RETURN_NON_STRICT(res, non_strict_f(expected1));

	BMOCK_STRICT {		
		BMOCK_EXPECT(g(expected1));	
		BMOCK_EXPECT(g(expected2));
		BMOCK_EXPECT(flex_tester_method());
	}

	BMOCK_REPLAY;
	
	BOOST_CHECK_NO_THROW(f(actual1,actual1));
	BOOST_CHECK_NO_THROW(g(actual1));

	BOOST_CHECK_NO_THROW(g(actual1));
		BOOST_CHECK_NO_THROW(res_actual = non_strict_f(actual1));		//non strict
	BOOST_CHECK_NO_THROW(g(actual2));
	BOOST_CHECK_NO_THROW(flex_tester_method());
	
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(res,res_actual);
}

BMOCK_TEST(flex_tester2, test_non_strict_wrong_parameters) {
	const std::string   s_actual     = boost::lexical_cast<std::string>(actual2);
	const std::string   s_expected   = boost::lexical_cast<std::string>(expected1);

	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__ + 9);

	__BMOCK_EXPECT_RETURN_C(c,true,t->check_equal(s_expected,s_actual,bmock::message(bmock::ARG_VALUE,"x"),loc));
	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->abort(loc));
	__BMOCK_REPLAY_C(c);
	{
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);__BMOCK_CREATE_MOCK_C((&c1),*);
		__BMOCK_NON_STRICT_C(c1) { 	
			__BMOCK_EXPECT_RETURN_C(c1, res, f(expected1,expected1));	
			__BMOCK_EXPECT_C(c1, g(expected1));
			__BMOCK_EXPECT_RETURN_C(c1, res, f(expected3, expected4));
		}
		__BMOCK_REPLAY_C(c1);
		BOOST_CHECK_THROW(g(actual2),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

BMOCK_TEST(flex_tester2, test_non_strict_with_calls_range) {
	BMOCK_NON_STRICT { 	
		BMOCK_EXPECT_RETURN(res, f(expected1,expected1));	
		BMOCK_EXPECT(g(expected1));
		BMOCK_REPEAT_RANGE(1,3);
	}

	BMOCK_EXPECT_RETURN_NON_STRICT(res, non_strict_f(expected1));

	BMOCK_STRICT {		
		BMOCK_EXPECT(g(expected3));	
		BMOCK_REPEAT_RANGE(2,4);
		BMOCK_EXPECT(g(expected4));		
		BMOCK_EXPECT(flex_tester_method());
	}

	BMOCK_REPLAY;

	BOOST_CHECK_NO_THROW(f(actual1,actual1));
	BOOST_CHECK_NO_THROW(g(actual1));

	BOOST_CHECK_NO_THROW(g(actual3));
	BOOST_CHECK_NO_THROW(g(actual3));
		BOOST_CHECK_NO_THROW(res_actual = non_strict_f(actual1));		//non strict	
	BOOST_CHECK_NO_THROW(g(actual4));
	BOOST_CHECK_NO_THROW(flex_tester_method());	

	BOOST_CHECK_EQUAL(res,res_actual);
	BMOCK_VERIFY;	
}

BMOCK_TEST(flex_tester2, test_non_strict_with_exceptions) {
	bool cought_bad_cast = false;
	bool cought_bad_typeid = false;

	BMOCK_NON_STRICT {
		BMOCK_EXPECT_THROW(new std::bad_cast, g(expected1));
		BMOCK_EXPECT_THROW(new std::bad_typeid, g(expected1));
		BMOCK_EXPECT_THROW(new std::exception, g(expected2));
	}
	BMOCK_REPLAY;

	BOOST_CHECK_THROW(g(expected2), std::exception);
	for (int i = 0; i < 2; i++) {
		try {
			g(expected1);
		} catch(const std::bad_cast&) {
			cought_bad_cast = true;
		} catch(const std::bad_typeid&) {
			cought_bad_typeid = true;
		}
	}
	BOOST_CHECK(cought_bad_typeid);
	BOOST_CHECK(cought_bad_cast);
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_non_strict__call_to_strict_non_strict_area_finished_min) {
	BMOCK_NON_STRICT {
		BMOCK_EXPECT(g(expected1));
		BMOCK_REPEAT_RANGE(1,3);
		BMOCK_EXPECT(g(expected2));		
		BMOCK_EXPECT(g(expected4));		
		BMOCK_REPEAT_RANGE(1,2);
	}
	BMOCK_STRICT {
		BMOCK_EXPECT(g(expected3));
	}
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(expected4));
	BOOST_CHECK_NO_THROW(g(expected2));
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected3));
	BMOCK_VERIFY;
}

BMOCK_TEST(flex_tester2, test_non_strict__call_to_non_strict_strict_area_finished_min) {
	BMOCK_STRICT {
		BMOCK_EXPECT(g(expected1));
		BMOCK_REPEAT_RANGE(1,3);
		BMOCK_EXPECT(g(expected2));		
		BMOCK_EXPECT(g(expected4));		
		BMOCK_REPEAT_RANGE(1,2);
	}
	BMOCK_NON_STRICT {
		BMOCK_EXPECT(g(expected3));
	}
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected1));
	BOOST_CHECK_NO_THROW(g(expected2));
	BOOST_CHECK_NO_THROW(g(expected4));
	BOOST_CHECK_NO_THROW(g(expected3));
	BMOCK_VERIFY;
}

